package iotwisp.base.orm;

import iotwisp.base.cqrs.BaseException;
import iotwisp.base.cqrs.BaseQO;
import iotwisp.base.orm.query.pagination.Pagination;
import iotwisp.base.tree.EMTreeData;
import iotwisp.base.tree.TreeDataBaseEntity;
import iotwisp.base.tree.TreeDataBaseQO;
import iotwisp.base.util.BeanUtils;
import iotwisp.base.util.JSONUtils;
import iotwisp.base.util.UUIDGenerator;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.*;
import org.hibernate.criterion.*;
import org.hibernate.internal.CriteriaImpl;
import org.hibernate.internal.CriteriaImpl.Subcriteria;
import org.hibernate.transform.AliasToBeanResultTransformer;
import org.hibernate.transform.ResultTransformer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * Hibernate基础查询DAO
 *
 * @param <T>
 * @param <QO>
 * @author
 */
@Transactional
public abstract class BaseDao<T extends BaseEntity, QO extends BaseQO> extends HibernateBaseDao<T, Serializable> {

    @Autowired
    private ApplicationContext applicationContext;

    private CommonDao commonDao;

    /**
     * HIBERNATE 的 userPlanOrder 属性
     */
    protected static final String ORDER_ENTRIES = "orderEntries";

    private CommonDao getCommonDao() {
        if (commonDao == null && applicationContext != null)
            commonDao = applicationContext.getBean(CommonDao.class);
        if (commonDao == null)
            throw new RuntimeException("CommonDao尚未初始化");
        return commonDao;
    }

    /**
     * 查询唯一对象
     *
     * @param qo
     * @return
     */
    @Transactional(readOnly = true)
    public T queryUnique(QO qo) {
        if (qo.getBatchResult()) {
            List<T> list = queryList(qo);
            if (list != null && list.size() > 0) {
                return list.get(0);
            }
        } else {
            List<T> list = queryList(qo, 1);
            if (list != null && list.size() > 0) {
                return list.get(0);
            }

        }

        return null;
    }

    /**
     * 查询对象列表
     *
     * @param qo
     * @return
     */
    @Transactional(readOnly = true)
    public List<T> queryList(QO qo) {
        return queryList(qo, 0, null);
    }

    /**
     * 查询实体列表
     *
     * @param qo
     * @param maxSize 最大获取数量
     * @return
     */
    @Transactional(readOnly = true)
    public List<T> queryList(QO qo, Integer maxSize) {
        return queryList(qo, 0, maxSize);
    }

    /**
     * 查询实体列表
     *
     * @param qo
     * @param offset  偏移量
     * @param maxSize 最大获取数量
     * @return
     */
    @Transactional(readOnly = true)
    @SuppressWarnings("unchecked")
    public List<T> queryList(QO qo, Integer offset, Integer maxSize) {

        // 涉及需要最大最小值当查询条件的，先查出最大最小值并设入QO中
        if (StringUtils.isNotBlank(qo.getMaxProperty())) {
            Object maxValue = maxProperty(qo.getMaxProperty(), qo);
            BeanUtils.setFieldValue(qo, qo.getMaxValueProperty(), maxValue);
        } else if (StringUtils.isNotBlank(qo.getMinProperty())) {
            Object maxValue = maxProperty(qo.getMinProperty(), qo);
            BeanUtils.setFieldValue(qo, qo.getMinValueProperty(), maxValue);
        }

        Criteria criteria = createCriteria(qo);
        if (offset != null && offset >= 0) {
            criteria.setFirstResult(offset);
        }
        if (maxSize != null && maxSize >= 1) {
            criteria.setMaxResults(maxSize);
        }
        return criteria.list();
    }

    /**
     * 查询数量
     *
     * @param qo
     * @return
     */
    @Transactional(readOnly = true)
    public Integer queryCount(QO qo) {
        Criteria criteria = createCriteria(qo);
        CriteriaImpl impl = (CriteriaImpl) criteria;
        BeanUtils.setFieldValue(impl, ORDER_ENTRIES, new ArrayList<Object>());

        // Object result = criteria.setProjection(Projections.rowCount())
        // .uniqueResult();

        Object result = criteria.setProjection(Projections.countDistinct("id")).uniqueResult();
        if (null != result) {
            int totalCount = ((Number) result).intValue();
            return totalCount;
        } else {
            return 0;
        }

    }

    /**
     * 分页查询对象
     *
     * @param pagination
     * @return
     */
    @Transactional(readOnly = true)
    @SuppressWarnings("unchecked")
    public Pagination queryPagination(Pagination pagination) {
        if (!(pagination.getCondition() instanceof BaseQO)) {
            pagination.setCondition(null);
        }
        Criteria criteria = createCriteria((QO) pagination.getCondition());
        Pagination resultPagination = super.findByCriteria(criteria, pagination);
        resultPagination.setCheckPassLastPageNo(pagination.isCheckPassLastPageNo());
        resultPagination.setCondition(pagination.getCondition());
        return resultPagination;
    }

    public Pagination queryPagination(QO qo) {
        Pagination pagination = new Pagination();
        pagination.setCondition(qo);
        pagination.setPageNo(qo.getPageNo());
        pagination.setPageSize(qo.getPageSize());
        pagination.setOffset(qo.getOffset());
        pagination.setSelectTotalCount(qo.getSelectCount());
        return queryPagination(pagination);
    }

    @Transactional(readOnly = true)
    @SuppressWarnings("unchecked")
    public <ASTYPE> List<ASTYPE> hqlQueryList(Class<ASTYPE> alias, String hql, List<Object> params) {

        Query query = getSession().createQuery(hql);

        if (params != null)
            for (int i = 0; i < params.size(); i++)
                query.setParameter(i, params.get(i));

        query.setResultTransformer(new AliasToBeanResultTransformer(alias));

        return query.list();
    }

    @Transactional(readOnly = true)
    public Pagination hqlQueryPagination(Class<?> alias, String hql, String countHql, Integer pageNo, Integer pageSize,
                                         Object condition, List<Object> params) {

        Long totalCount = 0L;

        Query countQuery = getSession().createQuery(countHql);

        for (int i = 0; i < params.size(); i++) {
            countQuery.setParameter(i, params.get(i));
        }
        totalCount = (Long) countQuery.uniqueResult();

        Pagination pagination = new Pagination(pageNo, pageSize, totalCount.intValue());

        if (totalCount > 0) {

            Query query = getSession().createQuery(hql);

            for (int i = 0; i < params.size(); i++)
                query.setParameter(i, params.get(i));

            query.setResultTransformer(new AliasToBeanResultTransformer(alias));
            query.setFirstResult(pagination.getFirstResult());
            query.setMaxResults(pageSize);

            List<?> list = query.list();
            pagination.setList(list);
        }

        if (pagination.getList() == null)
            pagination.setList(new ArrayList<Object>());
        if (pagination.getCondition() == null)
            pagination.setCondition(condition);

        return pagination;
    }

    private Criteria buildBaseCriteria(Criteria criteria, QO qo) {
        if (qo != null) {
            if (this instanceof CommonDao) {
                if (qo.getId() != null) {
                    criteria.add(Restrictions.eq("id", qo.getId()));
                }
                if (qo.getIdNotIn() != null && qo.getIdNotIn().length > 0) {
                    criteria.add(Restrictions.and(eachNeProperty(qo.getIdNotIn(), "id")));
                }
                if (qo.getIds() != null && qo.getIds().size() > 0) {
                    criteria.add(Restrictions.in("id", qo.getIds()));
                }
            } else {
                // 自动处理QO注解
                getCommonDao().buildCriteriaOut(criteria, qo);
            }
        }
        return criteria;
    }

    /**
     * 创建Hibernate标准查询
     *
     * @param qo
     * @return
     */
    protected Criteria createCriteria(QO qo) {

        Session session = super.getSession(qo.getOpenNewSession());
        Criteria criteria = session.createCriteria(getEntityClass());
        if (qo.isReadLock()) {
            criteria.setLockMode(LockMode.PESSIMISTIC_READ);
        }

        if (qo.isWriteLock()) {
            criteria.setLockMode(LockMode.PESSIMISTIC_WRITE);
        }

        if (qo.getBatchResult() != null && qo.getBatchResult()) {
            criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
        }

        // 留给具体dao实现为criteria进行进一步加工
        buildCriteria(criteria, qo);
        // 处理字段筛选投影
        buildIfProjectionResultCriteria(criteria, qo);
        // 处理qo中的查询条件
        buildBaseCriteria(criteria, qo);

        // 如果有投影，用我们自定义的转换器以实现完美赋值
        if (((CriteriaImpl) criteria).getProjection() != null) {
            criteria.setResultTransformer(new CustomProjectionResultTransformer(getEntityClass()));
        }

        return criteria;
    }

    public Criteria buildCriteriaOut(Criteria criteria, QO qo) {
        buildCriteria(criteria, qo);
        buildBaseCriteria(criteria, qo);
        if (!(this instanceof CommonDao)) {
            buildIfProjectionResultCriteria(criteria, qo);
        }
        return criteria;
    }

    /**
     * 为Hibernate标准查询添加条件
     *
     * @param criteria
     * @return
     */
    abstract protected Criteria buildCriteria(Criteria criteria, QO qo);

    /**
     * 执行HQL语句
     *
     * @param hql
     * @param params
     * @return
     */
    public int executeHql(String hql, Object... params) {
        Query query = getSession().createQuery(hql);
        for (int i = 0; i < params.length; i++) {
            query.setParameter(i, params[i]);
        }
        return query.executeUpdate();
    }

    /**
     * 根据HQL查询列表
     *
     * @param hql
     * @param offset
     * @param size
     * @param params
     * @return
     */
    public List<?> find(String hql, int offset, int size, Object... params) {
        Query query = getSession().createQuery(hql);
        for (int i = 0; i < params.length; i++) {
            query.setParameter(i, params[i]);
        }
        query.setFirstResult(offset);
        query.setMaxResults(size);
        return query.list();
    }

    /**
     * 分页查询
     *
     * @param hql
     * @param cntHql
     * @param pageNo
     * @param pageSize
     * @param values
     * @return
     */
    protected Pagination findPagination(String hql, String cntHql, int pageNo, int pageSize, Object... values) {
        Long totalCount = (Long) findUnique(cntHql, values);
        List<?> list = createQuery(hql, values).setFirstResult((pageNo - 1) * pageSize).setMaxResults(pageSize).list();
        Pagination pagination = new Pagination(pageNo, pageSize, totalCount.intValue(), list);
        return pagination;
    }

    public void save(T entity) {
        super.save(entity);
    }

    public void saveAll(List<Object> list) {
        super.saveAll(list);
    }

    public void updateAll(List<Object> list) {
        super.updateAll(list);
    }

    public void deleteAll(List<Object> list) {
        super.deleteAll(list);
    }

    public void saveOrUpdate(T entity) {
        super.saveOrUpdate(entity);
    }

    @SuppressWarnings("unchecked")
    public T merge(T entity) {
        return (T) super.getSession().merge(entity);
    }

    public void update(T entity) {
        super.update(entity);
    }

    public void delete(T entity) {
        super.delete(entity);
    }

    public void deleteById(Serializable id) {
        T entity = get(id);
        if (entity != null) {
            super.delete(get(id));
        }
    }

    public T get(Serializable id) {
        T entity = null;
        try {
            entity = super.get(id);
        } catch (Exception e) {
        }
        return entity;
    }

    public T load(Serializable id) {
        return super.load(id);
    }

    public T load(Serializable id, boolean lock) {
        return super.load(id, lock);
    }

    public T get(Serializable id, boolean lock) {
        T entity = null;
        try {
            entity = super.get(id, lock);
        } catch (Exception e) {
        }
        return entity;
    }

    public void lockUpgrade(T entity) {
        super.getSession().buildLockRequest(LockOptions.UPGRADE).lock(entity);
    }

    public void evict(T entity) {
        super.evict(entity);
    }

    public void saveArray(T... entitys) {
        for (T entity : entitys) {
            save(entity);
        }
    }

    public void saveList(List<T> list) {
        for (T entity : list) {
            save(entity);
        }
    }

    public void updateArray(T... entitys) {
        for (T entity : entitys) {
            update(entity);
        }
    }

    public void updateList(List<T> list) {
        for (T entity : list) {
            update(entity);
        }
    }

    public void deleteByIds(Serializable... ids) {
        for (Serializable id : ids) {
            deleteById(id);
        }
    }

    public void deleteByIds(List<Serializable> ids) {
        for (Serializable id : ids) {
            deleteById(id);
        }
    }

    public void flush() {
        super.getSession().flush();
    }

    public void clear() {
        super.getSession().clear();
    }

    public Double sumProperty(String propertyName, QO qo) {
        Criteria criteria = createCriteria(qo);
        CriteriaImpl impl = (CriteriaImpl) criteria;
        BeanUtils.setFieldValue(impl, ORDER_ENTRIES, new ArrayList<Object>());
        Number number = ((Number) criteria.setProjection(Projections.sum(propertyName)).uniqueResult());
        return number == null ? 0d : number.doubleValue();
    }

    public Double avgProperty(String propertyName, QO qo) {
        Criteria criteria = createCriteria(qo);
        CriteriaImpl impl = (CriteriaImpl) criteria;
        BeanUtils.setFieldValue(impl, ORDER_ENTRIES, new ArrayList<Object>());
        Number number = ((Number) criteria.setProjection(Projections.avg(propertyName)).uniqueResult());
        return number == null ? 0d : number.doubleValue();
    }

    @SuppressWarnings("unchecked")
    public Object maxProperty(String propertyName, QO qo) {
        Criteria criteria = createCriteria(qo);
        CriteriaImpl impl = (CriteriaImpl) criteria;
        BeanUtils.setFieldValue(impl, ORDER_ENTRIES, new ArrayList<Object>());
        return criteria.setProjection(Projections.max(propertyName)).uniqueResult();
    }

    /**
     * 遍历数组条件（代替IN查询）
     *
     * @param objs
     * @param propertyName
     * @return
     */
    protected Criterion[] eachEqProperty(Object[] objs, String propertyName) {
        Criterion[] criterions = new Criterion[objs.length];
        for (int i = 0; i < objs.length; i++) {
            criterions[i] = Restrictions.eq(propertyName, objs[i]);
        }
        return criterions;
    }

    /**
     * 遍历数组条件（代替NOT IN查询）
     *
     * @param objs
     * @param propertyName
     * @return
     */
    protected Criterion[] eachNeProperty(Object[] objs, String propertyName) {
        Criterion[] criterions = new Criterion[objs.length];
        for (int i = 0; i < objs.length; i++) {
            criterions[i] = Restrictions.ne(propertyName, objs[i]);
        }
        return criterions;
    }

    /**
     * 构建如果是指定字段的查询且必须指定别名
     *
     * @param criteria
     * @param qo
     */
    protected void buildIfProjectionResultCriteria(Criteria criteria, QO qo) {
        ProjectionList projectionList = Projections.projectionList();

        if (qo.getProjectionDistinctProperties() != null) {
            ProjectionList projectionList2 = Projections.projectionList();

            for (String property : qo.getProjectionDistinctProperties()) {
                projectionList2.add((Projections.property(property)), property);
            }

            projectionList.add(Projections.distinct(projectionList2), "dis");
            projectionList.add(Projections.property("id"), "id");
            criteria.setProjection(projectionList);
            criteria.setResultTransformer(new CustomProjectionResultTransformer(getEntityClass()));
        }
        //
        // //////////////////////////
        if (qo.getProjectionProperties() != null && qo.getProjectionProperties().length > 0) {
            if (criteria instanceof CriteriaImpl) {
                for (String property : qo.getProjectionProperties()) {
                    projectionList.add(Projections.property(property), property);
                }
                criteria.setProjection(projectionList);
                criteria.setResultTransformer(new CustomProjectionResultTransformer(getEntityClass()));
            } else if (criteria instanceof Subcriteria) {
                StringBuilder aliasSb = new StringBuilder("");
                StringBuilder propertySb = new StringBuilder("");
                Criteria preCriteria = criteria;
                while (preCriteria instanceof Subcriteria) {
                    Subcriteria currentSubcriteria = (Subcriteria) preCriteria;
                    currentSubcriteria.setAlias(qo.getAlias());
                    if (currentSubcriteria.getAlias() == null) {
                        currentSubcriteria.setAlias(currentSubcriteria.getPath());
                    }

                    System.out.println("###" + currentSubcriteria.getAlias());
                    aliasSb.insert(0, currentSubcriteria.getAlias());
                    propertySb.insert(0, currentSubcriteria.getPath());
                    preCriteria = currentSubcriteria.getParent();
                    if (preCriteria instanceof Subcriteria) {
                        aliasSb.insert(0, ".");
                        propertySb.insert(0, ".");
                    }
                }
                String alias = aliasSb.toString();
                String propertyPath = propertySb.toString();
                if (preCriteria instanceof CriteriaImpl) {
                    CriteriaImpl criteriaImpl = (CriteriaImpl) preCriteria;
                    if (criteriaImpl.getProjection() != null) {
                        projectionList = (ProjectionList) criteriaImpl.getProjection();
                    }
                    for (String property : qo.getProjectionProperties()) {
                        projectionList.add(Projections.property(alias + "." + property), propertyPath + "." + property);
                    }
                    ResultTransformer oldRtf = criteriaImpl.getResultTransformer();
                    criteriaImpl.setProjection(projectionList);
                    if (oldRtf != null) {
                        criteriaImpl.setResultTransformer(oldRtf);
                    }
                }
            } else {
                criteria.setProjection(projectionList);
            }
        }
    }

    // =============预排序遍历树实现===================

    /**
     * 添加一个新的树节点
     *
     * @param treeId       要添加节点的树标识
     * @param parentId     要添加子节点的父节点标识
     * @param leftId       要添加节点位置左边的节点，可选，默认添加到最右边
     * @param className    树形数据的业务实体类名
     * @param sourceTreeId 新节点是另一棵原有的树，可选
     * @return 返回添加后应保存的新树形节点数据
     * @throws BaseException
     */
    public EMTreeData addTreeNode(String treeId, String parentId, String leftId, String className, String sourceTreeId, String removeColumnName)
            throws BaseException {

        log.debug("树{}要添加新节点，目标父节点为{}，目标左节点为{}，类为{}，来源树为{}", treeId, parentId, leftId, className, sourceTreeId);
        // 该处只是生成了新节点数据，如果是添加新生成的单个节点，返回值交还的新节点数据一定要记得入库，否则树会有空缺
        EMTreeData newTreeData = new EMTreeData(treeId, true);

        TreeDataBaseEntity parent = null;

        if (StringUtils.isNotBlank(parentId)) {

            System.out.println(getEntityClass());
            parent = (TreeDataBaseEntity) get(getEntityClass(), parentId);
            if (parent != null) {
                // 因批量update导致一级缓存失效，清除后重新查询parent
                getSession().evict(parent);
                parent = (TreeDataBaseEntity) get(getEntityClass(), parentId);
            }
        }

        // 有父类型，检查父类型与前值是否合法
        if (parent != null) {
            log.debug("目标根存在");
            log.debug("目标根左值:{},右值:{}", parent.getTreeData().getLeftValue(), parent.getTreeData().getRightValue());

            if (!StringUtils.equals(treeId, parent.getTreeData().getTreeId())) {
                BaseException e = new BaseException(BaseException.PARAM_ILLEGALITY, "父节点与树id不符");
                log.error(e.getMessage(), e);
                throw e;
            }

            // 查出将要插入位置的左边节点
            TreeDataBaseEntity left = queryLeftNodeForCreateNode(parentId, null, treeId);
            log.debug("查出父节点{}下一级最右边的节点当做参照左节点", parentId);
            newTreeData = addTreeNodeForPosition(newTreeData, parent, left, sourceTreeId, className, removeColumnName);

        } else if (StringUtils.isNotBlank(leftId)) {

            // 查出将要插入位置的左边节点
            TreeDataBaseEntity left = queryLeftNodeForCreateNode(null, leftId, treeId);
            log.debug("查出给出的左节点{}", leftId);
            newTreeData = addTreeNodeForPosition(newTreeData, null, left, sourceTreeId, className, removeColumnName);
        } else {
            log.debug("目标不存在，添加了一个新的根节点");
        }

        return newTreeData;
    }

    /**
     * 以父节点和左节点为参照插入新节点或已有树
     *
     * @param newTreeData
     * @param parent
     * @param left
     * @param sourceTreeId
     * @param className
     * @return
     * @throws BaseException
     */
    private EMTreeData addTreeNodeForPosition(EMTreeData newTreeData, TreeDataBaseEntity parent,
                                              TreeDataBaseEntity left, String sourceTreeId, String className, String removeColumnName) throws BaseException {

        Integer deviation = null;
        String treeId = null;

        // 按要插入新节点还是原有树分别处理
        if (StringUtils.isBlank(sourceTreeId)) {

            log.debug("没给来源树id，插一个新节点");
            if (left == null) {

                // 新节点初始化，参照值为父节点左值
                newTreeData = new EMTreeData(parent.getTreeData().getTreeId(), parent.getId(),
                        parent.getTreeData().getLevel() + 1, parent.getTreeData().getLeftValue());
                deviation = parent.getTreeData().getLeftValue();
                log.debug("没有左节点，当作父节点下唯一节点来初始化");
                treeId = parent.getTreeData().getTreeId();
            } else {
                // 新节点初始化，参照值为左节点右值
                newTreeData = new EMTreeData(parent.getTreeData().getTreeId(), parent.getId(),
                        parent.getTreeData().getLevel() + 1, left.getTreeData().getRightValue());
                deviation = left.getTreeData().getRightValue();
                log.debug("有左节点，按插入在该左节点右边来初始化");
                treeId = left.getTreeData().getTreeId();
            }

            moveOldNodeForAdd(treeId, deviation, 2, className, removeColumnName);
        } else {
            log.debug("给了来源树id:{}，准备把来源树插到目标位置");
            // 算插入所需偏移度
            if (left == null) {
                log.debug("没给左节点:{}，目标位置为节点{}-{}的下一级最右边", sourceTreeId, parent.getTreeData().getLeftValue(),
                        parent.getTreeData().getRightValue());
                deviation = parent.getTreeData().getLeftValue();
                treeId = parent.getTreeData().getTreeId();
            } else {
                log.debug("给了左节点:{}，目标位置为节点{}-{}的右边", sourceTreeId, left.getTreeData().getLeftValue(),
                        left.getTreeData().getRightValue());
                deviation = left.getTreeData().getRightValue();
                treeId = left.getTreeData().getTreeId();
            }

            TreeDataBaseQO qoSource = new TreeDataBaseQO();
            qoSource.setTreeDataTreeId(sourceTreeId);
            qoSource.setTreeDataLeftValue(1);
            QO qo1 = (QO) JSONUtils.transform(qoSource, getQOClass());
            TreeDataBaseEntity sourceTreeRoot = (TreeDataBaseEntity) queryUnique(qo1);
            String sourceTreeRootId = sourceTreeRoot.getId();
            if (sourceTreeRoot != null) {
                getSession().evict(sourceTreeRoot);
                sourceTreeRoot = (TreeDataBaseEntity) get(getEntityClass(), sourceTreeRootId);
            }
            // 以新节点为参照，移动旧节点
            moveOldNodeForAdd(treeId, deviation, sourceTreeRoot.getTreeData().getWidth(), className, removeColumnName);

            log.debug("按目标位置计算出要给原树整体偏移{}", deviation);
            // 插原有树，重建整棵树
            rebuildTreeForAdd(className, sourceTreeId, deviation, parent.getTreeData().getTreeId(), removeColumnName);

            // 因批量update导致一级缓存失效，清除后重新查询sourceTreeRoot
            getSession().evict(sourceTreeRoot);
            sourceTreeRoot = (TreeDataBaseEntity) get(getEntityClass(), sourceTreeRootId);

            // 为插入好的新子树重建parentId和level
            handleParentAndLevel(sourceTreeRoot, parent, className, removeColumnName);

            String newTreeNodeId = sourceTreeRoot.getId();
            if (newTreeData != null) {
                sourceTreeRoot = (TreeDataBaseEntity) get(getEntityClass(), newTreeNodeId);
            }

            newTreeData = sourceTreeRoot.getTreeData();

        }

        return newTreeData;
    }

    private void handleParentAndLevel(TreeDataBaseEntity newTreeNode, TreeDataBaseEntity parent, String className, String removeColumnName) {

        newTreeNode.getTreeData().setParentId(parent.getId());
        Integer levelChange = parent.getTreeData().getLevel() + 1 - newTreeNode.getTreeData().getLevel();

        if (levelChange == 0) {
            return;
        }

        executeUpdate("update " + className
                        + " set level = level + " + levelChange + "  where treeData.treeId = ?0 and treeData.leftValue >= ?1 and treeData.rightValue <= ?2 "
                , newTreeNode.getTreeData().getTreeId(), newTreeNode.getTreeData().getLeftValue(), newTreeNode.getTreeData().getRightValue());
    }

    /**
     * 移动一个已有树节点到一个新的父节点下，可选跟随的左节点
     *
     * @param move         要移动的节点
     * @param targetParent 要移动到的目标位置的父节点
     * @param targetLeftId 要移动到的目标位置的左边节点，可选，默认移动到最右边
     * @return 返回移动后应保存的新树形节点数据
     * @throws BaseException
     */
    protected void moveTreeNode(TreeDataBaseEntity move, TreeDataBaseEntity targetParent, String targetLeftId,
                                String className, String removeColumnName) throws BaseException {

        if (move.getTreeData().isUpNode(targetParent)) {
            throw new BaseException(BaseException.CAN_NOT_MOVE_TO_CHILD, "不能将节点移动到它的下级节点下");
        }

        String treeId = move.getTreeData().getTreeId();
        String tempTreeId = UUIDGenerator.getUUID();

        // 把要移除的节点及其子节点从原树移出，转移成一棵新树
        deleteTreeNode(move, className, tempTreeId, removeColumnName);

        // 把新树插入到原树中的新位置
        addTreeNode(treeId, targetParent.getId(), targetLeftId, className, tempTreeId, removeColumnName);
    }

    /**
     * 要往父节点子级插入新节点时，查询应插入位置的左边节点
     *
     * @param parentId
     * @param leftId
     * @return
     */
    private TreeDataBaseEntity queryLeftNodeForCreateNode(String parentId, String leftId, String treeId) {

        if (StringUtils.isBlank(parentId)) {
            return null;
        }

        TreeDataBaseEntity left = null;

        TreeDataBaseQO qo = new TreeDataBaseQO();
        qo.setTreeDataTreeId(treeId);

        if (StringUtils.isBlank(leftId)) {
            // 如果没有指定左节点，就查最右边的节点当左节点
            qo.queryRightEndNode(parentId);
        } else {
            // 指定了左节点
            qo.setId(leftId);
        }

        // 查出同级左边紧挨的一个节点
        QO qo2 = (QO) JSONUtils.transform(qo, getQOClass());
        left = (TreeDataBaseEntity) queryUnique(qo2);
        return left;

    }

    protected void deleteTreeNode(TreeDataBaseEntity remove, String className, String tempTreeId, String removeColumnName) {

        log.debug("将节点{}移出到新树{}，左值为{}，右值为{}", remove.getId(), tempTreeId, remove.getTreeData().getLeftValue(),
                remove.getTreeData().getRightValue());
        if (StringUtils.isBlank(tempTreeId)) {

            log.debug("没有给临时树id，直接删除所有子节点");
            // 没有给临时树id，直接删除所有子节点
            executeUpdate(
                    "delete from " + className
                            + " where treeData.treeId = ?0 and treeData.leftValue > ?1 and treeData.rightValue < ?2",
                    remove.getTreeData().getTreeId(), remove.getTreeData().getLeftValue(),
                    remove.getTreeData().getRightValue());
            log.debug("移除了左值大于{}, 右值小于{}的所有节点");
        } else {

            log.debug("给了临时树id，将所有子节点移到临时树{}", tempTreeId);
            // 用新的临时树id，将移出来的树重排一下，保持临时树的完整性
            rebuildTreeForRemove(remove, className, tempTreeId, removeColumnName);
        }

        // 调整移除后的原树
        moveOldNodeForRemove(remove, className, removeColumnName);

    }

    protected void removeTreeNode(TreeDataBaseEntity remove, String className, String tempTreeId,
                                  String removeColumnName) {

        log.debug("将节点{}移出到新树{}，左值为{}，右值为{}", remove.getId(), tempTreeId, remove.getTreeData().getLeftValue(),
                remove.getTreeData().getRightValue());
        if (StringUtils.isBlank(tempTreeId)) {

            log.debug("没有给临时树id，直接删除所有子节点");
            // 没有给临时树id，直接删除所有子节点
            String removeStatusSQL = "";
            if (StringUtils.isNotBlank(removeColumnName)) {
                removeStatusSQL = "and " + removeColumnName + " = 'N'";
            }

            executeUpdate("update " + className
                            + " set remove = 'Y' where treeData.treeId = ?0 and treeData.leftValue > ?1 and treeData.rightValue < ?2 "
                            + removeStatusSQL, remove.getTreeData().getTreeId(), remove.getTreeData().getLeftValue(),
                    remove.getTreeData().getRightValue());
            log.debug("移除了左值大于{}, 右值小于{}的所有节点");
        } else {

            log.debug("给了临时树id，将所有子节点移到临时树{}", tempTreeId);
            // 用新的临时树id，将移出来的树重排一下，保持临时树的完整性
            rebuildTreeForRemove(remove, className, tempTreeId, removeColumnName);
        }

        // 调整移除后的原树
        moveOldNodeForRemove(remove, className, removeColumnName);

    }

    /**
     * 将树中移出来的一个分支重建一棵树
     *
     * @param remove
     * @param className
     * @param newTreeId
     */
    private void rebuildTreeForRemove(TreeDataBaseEntity remove, String className, String newTreeId,
                                      String removeColumnName) {

        log.debug("重排一下被移出来的临时树{}", newTreeId);
        // 算归位为根节点所需偏移度
        Integer deviation = remove.getTreeData().getLeftValue() - 1;
        log.debug("临时树所有节点需要向左偏移{}", deviation);
        // 全体减左右值
        String removeStatusSQL = "";
        if (StringUtils.isNotBlank(removeColumnName)) {
            removeStatusSQL = "and " + removeColumnName + " = 'N'";
        }
        executeUpdate("update " + className
                        + " set treeData.leftValue=treeData.leftValue - ?0, treeData.rightValue=treeData.rightValue - ?1, treeData.treeId = ?2 where treeData.treeId = ?3 and treeData.leftValue >= ?4 and treeData.rightValue <= ?5 "
                        + removeStatusSQL, deviation, deviation, newTreeId, remove.getTreeData().getTreeId(),
                remove.getTreeData().getLeftValue(), remove.getTreeData().getRightValue());
    }

    /**
     * 往目标树中整体移入的一棵树重建左右值
     *
     * @param className
     * @param sourceTreeId
     * @param deviation
     * @param targetTreeId
     */
    private void rebuildTreeForAdd(String className, String sourceTreeId, Integer deviation, String targetTreeId,
                                   String removeColumnName) {

        // 全体加左右值并更换树id
        String removeStatusSQL = "";
        if (StringUtils.isNotBlank(removeColumnName)) {
            removeStatusSQL = "and " + removeColumnName + " = 'N'";
        }
        executeUpdate("update " + className
                + " set treeData.leftValue=treeData.leftValue + ?0, treeData.rightValue=treeData.rightValue + ?1, treeData.treeId = ?2 where treeData.treeId = ?3 "
                + removeStatusSQL, deviation, deviation, targetTreeId, sourceTreeId);

        log.debug("原有树已插入到目标树中，所有节点偏移{}，树节点从{}并入{}", deviation, sourceTreeId, targetTreeId);
    }

    /**
     * 删掉了一个节点，移动整棵树中受影响的旧节点
     *
     * @param remove    要删除的节点
     * @param className 树形数据的业务实体类名
     */
    private void moveOldNodeForRemove(TreeDataBaseEntity remove, String className, String removeColumnName) {
        // 要同时删掉所有子节点，所以只移删除节点右边的，不移原有子节点，减去这个原节点的宽度（右-左-1）

        String removeStatusSQL = "";
        if (StringUtils.isNotBlank(removeColumnName)) {
            removeStatusSQL = "and " + removeColumnName + " = 'N'";
        }

        // 减左值
        executeUpdate("update " + className
                        + " set treeData.leftValue=treeData.leftValue - ?0 where treeData.treeId = ?1 and treeData.leftValue > ?2 "
                        + removeStatusSQL, remove.getTreeData().getWidth(), remove.getTreeData().getTreeId(),
                remove.getTreeData().getRightValue());
        // 减右值
        executeUpdate("update " + className
                        + " set treeData.rightValue=treeData.rightValue - ?0 where treeData.treeId = ?1 and treeData.rightValue > ?2 "
                        + removeStatusSQL, remove.getTreeData().getWidth(), remove.getTreeData().getTreeId(),
                remove.getTreeData().getRightValue());

    }

    /**
     * 将要添加新节点，移动整棵树中的旧节点腾出空间
     *
     * @param treeId    要移动的树id
     * @param deviation 起始偏移度
     * @param width     移动宽度
     * @param className 树形数据的业务实体类名
     * @throws BaseException
     */
    protected void moveOldNodeForAdd(String treeId, Integer deviation, Integer width, String className,
                                     String removeColumnName) throws BaseException {

        String removeStatusSQL = "";
        if (StringUtils.isNotBlank(removeColumnName)) {
            removeStatusSQL = "and " + removeColumnName + " = 'N'";
        }

        // 把所有左值大于等于偏移度的，都+宽度
        executeUpdate("update " + className
                + " set treeData.leftValue=treeData.leftValue + ?0 where treeData.treeId = ?1 and treeData.leftValue > ?2 "
                + removeStatusSQL, width, treeId, deviation);
        // 所有右值大于偏移度的，都+宽度
        executeUpdate("update " + className
                + " set treeData.rightValue=treeData.rightValue + ?0 where treeData.treeId = ?1 and treeData.rightValue > ?2 "
                + removeStatusSQL, width, treeId, deviation);
        log.info("新节点宽度:{}，左值:{}，右值:{}", width, deviation, deviation);

    }
}
